import type { Node, Schema } from "@rivet/model";
import {
  type Draft,
  type Patch,
  type PatchListener,
  produceWithPatches,
  type WritableDraft,
} from "immer";
const stepsByID: {
  [id: string]: { fromJSON(schema: Schema, json: any): Step };
} = Object.create(null);

export abstract class Step {
  changes: Patch[] = [];
  inverse: Patch[] = [];
  ///将此步骤应用于给定的文档，返回一个结果
  ///如果步骤不能执行，则该对象表示失败
  ///应用于此文档，或者通过包含一个
  ///转换后的文档。
  abstract apply(draft: Draft<Node> | null, doc: Node): StepResult | undefined;

  merge(_other: Step): Step | null {
    return null;
  }

  static fromJSON(schema: Schema, json: any): Step {
    if (!json || !json.stepType) {
      throw new RangeError("来自 Step.fromJSON 的json无效");
    }
    const type = stepsByID[json.stepType];
    if (!type) throw new RangeError(` ${json.stepType} 没有定义`);
    return type.fromJSON(schema, json);
  }

  static jsonID(
    id: string,
    stepClass: { fromJSON(schema: Schema, json: any): Step },
  ): any {
    if (id in stepsByID) throw new RangeError("重复使用步骤 JSON ID " + id);
    stepsByID[id] = stepClass;
    (stepClass as any).prototype.jsonID = id;
    return stepClass;
  }
}

export class StepResult {
  doc: Node | null;
  failed: string;

  constructor(
    doc: Node | null,
    failed: string | null,
  ) {
    this.doc = doc;
    this.failed = failed || "";
  }

  static ok(doc: Node): StepResult {
    return new StepResult(doc, null);
  }

  static fail(message: string): StepResult {
    return new StepResult(null, message);
  }

  static fromReplace(
    doc: Node,
    updater: (value: WritableDraft<Node>) => void,
    listener: PatchListener,
  ): StepResult {
    try {
      const [nextState, patches, inversePatches] = produceWithPatches<Node>(
        doc,
        (draftState) => {
          updater(draftState);
        },
      );
      listener(patches, inversePatches);
      return StepResult.ok(nextState);
    } catch (e: unknown) {
      return StepResult.fail((e as Error).message);
    }
  }
}
